package cc.android.testapp.cfg;

import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Pair;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import cc.android.testapp.TAContentProvider;
import cc.android.testapp.util.CLog;
import cc.android.testapp.xp.HookBase;
import cc.android.testapp.xp.hooker.adblock.util.ADBCfg;
import cc.commons.util.FileUtil;
import cc.commons.util.reflect.FieldUtil;

public class ConfigHelper extends BroadcastReceiver implements TAContentProvider.TAReslove {
    public static String PATH_CONFIG = "/config";
    public static String PATH_READ_FILE = "/read_file";
    public static String KEY_NAME = "KEY_NAME";
    private static WeakReference<Context> mContext = null;
    private static Uri mConfigUri;
    private static boolean mInited;
    /**
     * 客户端接收到的配置文件
     */
    private static String mPreCfgStr = "";
    /**
     * 最后一次读取的配置文件时间,用于检查缓存的配置是否为最新
     */
    private static long mLastReadTime = -1;
    /**
     * 缓存的配置文件,用于发送的客户端
     */
    private static String mLastReadConfig = "";

    /**
     * 用于在配置更新时回调
     */
    public static final HashMap<IConfig, HashMap<Field, Pair<String, String>>> mListeners = new HashMap<>();

    public static String MSG_UPDATE_CONFIG = Config.MODULE_PACKAGE_NAME + "/update_config";

    @Override
    public void onReceive(Context context, Intent pIntent) {
        String tAct = pIntent.getAction();
        if (MSG_UPDATE_CONFIG.equals(tAct)) {
            loadConfig();
        }
    }

    @Override
    public String handleRequest(String pPName, String pType, Uri pUri) {
        if (PATH_CONFIG.equals(pType)) {
            File tFile = Config.getConfigFile();
            try {
                if (tFile.exists()) {
                    if (tFile.lastModified() != mLastReadTime) {
                        mLastReadTime = tFile.lastModified();
                        JSONObject tJson = new JSONObject(FileUtil.readContent(tFile));
                        JSONObject tAdb = tJson.optJSONObject(ADBCfg.getCfgNode());
                        if (tAdb != null && ADBCfg.getWhiteList().contains(pPName)) {
                            tAdb.put(ADBCfg.KEY_WHITLE_NAME, true);
                        }
                        return mLastReadConfig = tJson.toString();
                    } else return mLastReadConfig;
                }
                return "{}";
            } catch (Throwable e) {
                CLog.log("Error on read config for remote", e);
            }
        } else if (PATH_READ_FILE.equals(pType)) {
            File tFile = new File(Config.config_dir, pUri.getQueryParameter(KEY_NAME));
            if (tFile.exists()) return FileUtil.readContent(tFile);
            return "";
        }
        return null;
    }

    public static String readRemoteFile(String pName) {
        return resolveConfig(PATH_READ_FILE, KEY_NAME, pName);
    }

    private static String resolve(Uri uri) {
        String tResult = null;
        int i = 3;
        while (i-- > 0 && tResult == null) {
            tResult = mContext.get().getContentResolver().getType(uri);
        }
        return tResult;
    }

    private static void loadConfig() {
        try {
            String tCfgStr = resolveConfig();
            if (!TextUtils.isEmpty(tCfgStr) && !mPreCfgStr.equals(tCfgStr)) {
                CLog.log("load config :" + tCfgStr);
                Config.setClientConfig(mPreCfgStr = TextUtils.isEmpty(tCfgStr) ? "{}" : tCfgStr);
                ConfigHelper.callConfigUpdated();
                //new Handler(Looper.getMainLooper()).post(ConfigHelper::callConfigUpdated);
            }
        } catch (Throwable e) {
            CLog.log("Error on load config", e);
        }

    }

    public static void callConfigUpdated() {
        for (Map.Entry<IConfig, HashMap<Field, Pair<String, String>>> sEntry : mListeners.entrySet()) {
            IConfig tCfg = sEntry.getKey();

            try {
                tCfg.onConfigChange();
            } catch (Throwable e) {
                String tName = (tCfg instanceof HookBase) ? ((HookBase) tCfg).getModuleName() : tCfg.getClass().getSimpleName();
                CLog.log("error on load config at module \"" + tName + "\"", e);
            }
        }
    }

    public static String resolveConfig() {
        Uri.Builder tBuilder = mConfigUri.buildUpon();
        tBuilder.path(PATH_CONFIG);
        return resolve(tBuilder.build());
    }

    public static String resolveConfig(String pPath, String pKey, String pValue) {
        return resolveConfig(pPath, new String[]{pKey}, pValue);
    }

    public static String resolveConfig(String pPath, String[] pKeys, String... pValues) {
        Uri.Builder tUpon = mConfigUri.buildUpon();
        tUpon.path(pPath);
        for (int i = 0; i < pKeys.length; i++) {
            tUpon.appendQueryParameter(pKeys[i], pValues[i]);
        }

        return resolve(tUpon.build());
    }

    public static void registerResolve() {
        ConfigHelper t = new ConfigHelper();
        TAContentProvider.registerReslove(ConfigHelper.PATH_CONFIG, t);
        TAContentProvider.registerReslove(ConfigHelper.PATH_READ_FILE, t);
    }

    public static void registerConfigLis(ConfigHelper.IConfig pLis) {
        mListeners.put(pLis, ConfigVPaser.resloveCfgAnno(pLis));
    }

    public static JSONObject getModuleCfgNode(IConfig pCfgM) {
        String tNodeKey = pCfgM.getCfgNode();
        JSONObject tNode = null;
        if (tNodeKey.isEmpty()) tNode = Config.getConfig();
        else if (Config.getConfig().has(pCfgM.getCfgNode())) {
            try {
                tNode = Config.getConfig().getJSONObject(pCfgM.getCfgNode());
            } catch (JSONException ignore) {
            }
        }

        return tNode;
    }

    public static void init(Context pCon) {
        if (mInited) return;
        mInited = true;


        if (pCon instanceof Application) {
            mContext = new WeakReference<>(pCon);
        } else {
            mContext = new WeakReference<>(pCon.getApplicationContext());
        }

        mConfigUri = Uri.parse("content://" + Config.MODULE_PACKAGE_NAME + "?pn=" + mContext.get().getPackageName());
        loadConfig();
    }

    /**
     * 配置模块,在hook模块注册时会自动注册配置监听
     */
    public interface IConfig {
        /**
         * 用于模块自定义的配置加载,在注解配置加载后调用
         */

        default void onConfigChange() {
            JSONObject tJson = ConfigHelper.getModuleCfgNode(this);
            if (tJson == null) return;
            onConfigChange(tJson);
        }

        default void onConfigChange(JSONObject pNode) {
            loadAnnoConfig(pNode);
        }

        default void loadAnnoConfig(JSONObject pNode) {
            HashMap<Field, Pair<String, String>> tAnnos = mListeners.get(this);
            if (tAnnos == null) return;

            for (Map.Entry<Field, Pair<String, String>> sEntry2 : tAnnos.entrySet()) {
                try {
                    String tStrV = pNode.optString(sEntry2.getValue().first, sEntry2.getValue().second);
                    FieldUtil.setFieldValue(sEntry2.getKey(), this, ConfigVPaser.paser(sEntry2.getKey().getType(), tStrV));
                } catch (Throwable e) {
                    String tName = (this instanceof HookBase) ? ((HookBase) this).getModuleName() : this.getClass().getSimpleName();
                    CLog.log("error on set config at module \"" + tName + "\" for field \"" + sEntry2.getKey().getName() + "\"", e);
                }
            }
        }

        /**
         * 当前模块的配置节点
         * <p>如果返回空字符串,则使用根节点配置<p/>
         *
         * @return 节点名字
         */
        String getCfgNode();
    }

}
